﻿#pragma once

#include "Misc/EngineEditorMacros.h"
#include "Containers/String.h"
#include "Math/MathBase.h"
#include "Containers/ScriptDelegateImplement.h"
#include "Math/Math.h"
#include "PieceTable.m.h"

//use a piece table to store text
//and a list of piece table node to point to the text
//https://link.zhihu.com/?target=https%3A//code.visualstudio.com/blogs/2018/03/23/text-buffer-reimplementation

//text buffer store the text
//and have two kinds of text buffer
//one is the original text buffer, it store the original text, and it is read only
//another is the user text buffer, it store the text that user input, and it is only appendable

RSTRUCT()
struct STextBufferPosition
{
    ROBJECT

public:

    RFIELD()
    int64_t LineIndex = 0;

    RFIELD()
    int64_t ColumnIndex = 0;
};

RSTRUCT()
struct STextBuffer
{
    ROBJECT

public:
    RFIELD()
    CString Text;

    //the start char index of each line
    //one buffet at least has one line
    RFIELD()
    TVector<int64_t> LineStarts;

    STextBufferPosition GetLineAndColumnIndex(int64_t InCharIndex) const
    {
        STextBufferPosition Position;
        if(InCharIndex==0)
        {
            return Position;
        }
        assert(InCharIndex>0 && InCharIndex<Text.GetLength() );

        for(int64_t i=0;i<LineStarts.Num();i++)
        {
            if(InCharIndex<LineStarts[i])
            {
                Position.LineIndex=i-1;
                Position.ColumnIndex=InCharIndex-LineStarts[i-1];
                return Position;
            }
        }

        Position.LineIndex=LineStarts.Num()-1;
        Position.ColumnIndex=InCharIndex-LineStarts.Last();


        return Position;
    
    }

    bool IsLineFeed(const STextBufferPosition & InPosition) const
    {
        return Text[LineStarts[InPosition.LineIndex]+InPosition.ColumnIndex]==U'\n';
    }
};


RSTRUCT()
struct SPieceTableNode
{
    ROBJECT

public:
    //the index of the buffer in the text buffers
    RFIELD()
    int64_t BufferIndex = 0;

    //the start char position in the buffer
    RFIELD()
    STextBufferPosition Start;

    RFIELD()
    STextBufferPosition End;

    //the number of char in the node
    RFIELD()
    int64_t Length = 0;

    //the number of line feed in the node
    RFIELD()
    int64_t LineFeedCount = 0;


};

RSTRUCT()
struct SDocumentPosition
{
    ROBJECT

public:
    SDocumentPosition()=default;

    SDocumentPosition(int64_t InLineIndex,int64_t InColumnIndex)
        :LineIndex(InLineIndex)
        ,ColumnIndex(InColumnIndex)
    {
    }

    RFIELD()
    int64_t LineIndex = 0;

    //the char index in line after curosr
    //so the max ColumnIndex==CharNum in line (exclude '\n')
    RFIELD()
    int64_t ColumnIndex = 0;

    bool operator==(const SDocumentPosition& Other) const
    {
        return LineIndex==Other.LineIndex && ColumnIndex==Other.ColumnIndex;
    }   

    //比较两个位置的大小，如果当前位置在Other位置之后，返回true
    bool operator>(const SDocumentPosition& Other) const
    {
        if(LineIndex>Other.LineIndex)
        {
            return true;
        }
        if(LineIndex<Other.LineIndex)
        {
            return false;
        }
        return ColumnIndex>Other.ColumnIndex;
    }

    bool operator>= (const SDocumentPosition& Other) const
    {
        if(LineIndex>Other.LineIndex)
        {
            return true;
        }
        if(LineIndex<Other.LineIndex)
        {
            return false;
        }
        return ColumnIndex>=Other.ColumnIndex;        
    }

    bool operator<(const SDocumentPosition& Other) const
    {
        if(LineIndex<Other.LineIndex)
        {
            return true;
        }
        if(LineIndex>Other.LineIndex)
        {
            return false;
        }
        return ColumnIndex<Other.ColumnIndex;
    }
};

struct SPieceTableNodePosition
{
    SPieceTableNodePosition()=default;

    SPieceTableNodePosition(int64_t InNodeIndex,int64_t InCharIndex)
        :NodeIndex(InNodeIndex)
        ,CharIndex(InCharIndex)
    {
    }

    int64_t NodeIndex = 0;

    //the char index in the node
    int64_t CharIndex = 0;

    bool IsValid() const
    {
        return NodeIndex>=0;
    }
};

class CPieceTableCommandBase
{
public:
    enum class ECommandType
    {
        Insert,
        Delete,
        Replace,  //替换
        ReplaceBatch, //批量替换
    };

    virtual ECommandType GetType() const = 0;
};

class CPieceTableInsertCommand : public CPieceTableCommandBase
{
public:
    ECommandType GetType() const override
    {
        return ECommandType::Insert;
    }

    //the text to insert
    CString Text;

    //the start char index in the document
    //after the command executed
    //also is insert position
    int64_t InsertPosition;

    int64_t GetEndCharIndexAfterInsert()
    {
        return InsertPosition+Text.GetLength();
    }
};

class CPieceTableDeleteCommand : public CPieceTableCommandBase
{
public:
    ECommandType GetType() const override
    {
        return ECommandType::Delete;
    }

    //the text to remove
    //will be used for redo
    CString Text;

    //the start char index in the document
    //before the command executed
    int64_t StartCharIndex;

    //the end char index in the document
    //before the command executed
    //inclusive
    int64_t EndCharIndex;
};

class CPieceTableReplaceCommand : public CPieceTableCommandBase
{
public:
    ECommandType GetType() const override
    {
        return ECommandType::Replace;
    }

    //the text that will be replaced
    CString From;

    //the text will be replaced with
    CString To;

    //the start char index in the document
    //it is the start char index of the FROM text befor executing command
    //包含
    int64_t StartCharIndex;

    //the end char index in the document
    //it is the end char index of the FROM text befor executing command
    //包含
    int64_t EndCharIndex;

};

class CPieceTableReplaceBatchCommand : public CPieceTableCommandBase
{
public:
    ECommandType GetType() const override
    {
        return ECommandType::ReplaceBatch;
    }

    //the text that will be replaced
    CString From;

    //the text will be replaced with
    CString To;

    //the start char index in the document
    //it is the start char index of the FROM text befor executing command
    //包含
    TVector<int64_t> StartCharIndices;

    //the end char index in the document
    //it is the end char index of the FROM text befor executing command
    //包含
    TVector<int64_t> EndCharIndices;

};



RSTRUCT()
/// @brief 
struct SPieceTable
{
    ROBJECT

public:
    RFIELD()
    TScriptDelegate<void()> OnDocumentChanged;

    //the first buffer is the original text buffer,it will be read only
    //the left buffers is the user text buffer, it will be appendable
    RFIELD()
    TVector<STextBuffer> Buffers;
    
    //TODO use a black-red tree to store the nodes
    //so that we can find the node by the line index more quickly
    RFIELD()
    TVector<SPieceTableNode> Nodes;

    TVector<std::shared_ptr<CPieceTableCommandBase>> CommandQueue;
    //newest applied command index
    int64_t LastAppliedCommandIndex = -1;

    //the last command that is applied when the document is saved
    //is no command is applied, it is nullptr
    std::shared_ptr<CPieceTableCommandBase> LastCommandOnSave=nullptr;

    TOptional<CString> CachedDocumentText;
    TOptional<int64_t> CachedLineCount;
    struct STextOfRange
    {
        //search range
        int64_t StartLineIndex;
        int64_t EndLineIndex;


        int64_t OutStartCharIndex;
        int64_t OutEndCharIndex; //inclusive
        CString Text;
    };
    STextOfRange* CachedTextOfRange=nullptr;

    //is document different from the last saved document
    bool IsDocumentChanged() const
    {
        if(LastAppliedCommandIndex==-1)
        {
            if(LastCommandOnSave==nullptr)
            {
                //the document is not changed
                return false;
            }
            return true;
        }
        return LastCommandOnSave!=CommandQueue[LastAppliedCommandIndex];
    }

    //let the piecetable know that the document is saved
    //should be called after the document is saved
    void NotifyDocumentSaved()
    {
        if(LastAppliedCommandIndex!=-1)
        {
            LastCommandOnSave=CommandQueue[LastAppliedCommandIndex];
        }
        else
        {
            LastCommandOnSave=nullptr;
        }
    }

    void PostTextChanged()
    {
        CachedDocumentText.Reset();
        CachedLineCount.Reset();
        if(CachedTextOfRange)
        {
            delete CachedTextOfRange;
            CachedTextOfRange=nullptr;
        }
        OnDocumentChanged.Broadcast();
    }

    int64_t GetLineCount() 
    {
        if(CachedLineCount.HasValue())
        {
            return CachedLineCount.GetValue();
        }

        int64_t LineCount = 1;

        for(auto& Node: Nodes)
        {
            LineCount+=Node.LineFeedCount;
        }

        CachedLineCount=LineCount;
        return LineCount;
    }

    CString GetText() 
    {
        if(CachedDocumentText.HasValue())
        {
            return CachedDocumentText.GetValue();
        }

        CString Text;
        int64_t Size=0;
        for(auto& Node: Nodes)
        {
            Size+=Node.Length;
        }
        Text.Reserve(Size);

        for(auto& Node: Nodes)
        {
            Text+=Buffers[Node.BufferIndex].Text.SubString(
                Buffers[Node.BufferIndex].LineStarts[Node.Start.LineIndex]+Node.Start.ColumnIndex
                ,Node.Length
            );
        }

        CachedDocumentText=Text;
        return Text;
    }

    /// @brief find the first node that contains the line index
    /// @param InLineIndex 
    /// @return  the position of the node
    SPieceTableNodePosition FindFirstNodeAtLine(int64_t InLineIndex) const
    {
        int64_t LineIndex = 0;
        for(int64_t i=0;i<Nodes.Num();i++)
        {
            auto& Node = Nodes[i];
            int64_t DeltaLineIndex = Node.End.LineIndex-Node.Start.LineIndex;
            if((LineIndex+DeltaLineIndex)>=InLineIndex)
            {
                //the node contains the line index
                //find the char index in the node
                int64_t LineInBuffer=InLineIndex-LineIndex + Node.Start.LineIndex;
                int64_t StartCharIndex = Buffers[Node.BufferIndex].LineStarts[LineInBuffer];
                if(LineInBuffer==Node.Start.LineIndex)
                {
                    StartCharIndex+=Node.Start.ColumnIndex;
                }
                int64_t CharIndexInNode=StartCharIndex-Buffers[Node.BufferIndex].LineStarts[Node.Start.LineIndex]-Node.Start.ColumnIndex;
                
                return SPieceTableNodePosition(
                    i
                    ,CharIndexInNode
                );

            }

            LineIndex+=DeltaLineIndex;
            //check if the last char of the node is '\n'
            auto& Buffer = Buffers[Node.BufferIndex];
            int64_t LastCharIndex=Buffer.LineStarts[Node.End.LineIndex]+
                Node.End.ColumnIndex;
            if(Buffer.Text[LastCharIndex]==U'\n')
            {
                LineIndex++;
            }

            continue;
        }

        return SPieceTableNodePosition(-1,0);
    }

    CString GetTextOfRange(
        int64_t InStartLineIndex
        ,int64_t InEndLineIndex
        ,int64_t& OutStartCharIndex
        ,int64_t & OutEndCharIndex //inclusive
    )
    {
        if(CachedTextOfRange)
        {
            if(CachedTextOfRange->StartLineIndex==InStartLineIndex
             && CachedTextOfRange->EndLineIndex==InEndLineIndex)
            {
                OutStartCharIndex=CachedTextOfRange->OutStartCharIndex;
                OutEndCharIndex=CachedTextOfRange->OutEndCharIndex;
                return CachedTextOfRange->Text;
            }
        }

        CachedTextOfRange=new SPieceTable::STextOfRange();
        CachedTextOfRange->StartLineIndex=InStartLineIndex;
        CachedTextOfRange->EndLineIndex=InEndLineIndex;

        assert(InStartLineIndex>=0 && InStartLineIndex<=InEndLineIndex );

        auto PositionAtStart = FindFirstNodeAtLine(InStartLineIndex);
        if(!PositionAtStart.IsValid())
        {
            // the start line index is out of range
            OutStartCharIndex=-1;
            OutEndCharIndex=-1;

            CachedTextOfRange->OutEndCharIndex=-1;
            CachedTextOfRange->OutStartCharIndex=-1;
            CachedTextOfRange->Text=U"";

            return CachedTextOfRange->Text;
        }

        auto PositionAfterEnd = FindFirstNodeAtLine(InEndLineIndex+1);

        //calculate the start char index
        OutStartCharIndex=PositionAtStart.CharIndex;
        for(int64_t i=0;i<PositionAtStart.NodeIndex;i++)
        {
            OutStartCharIndex+=Nodes[i].Length;
        }

        //calculate the end char index
        OutEndCharIndex=0;
        if(!PositionAfterEnd.IsValid())
        {
            //end char index is the last char of the last node
            for(auto& Node: Nodes)
            {
                OutEndCharIndex+=Node.Length;
            }
            OutEndCharIndex--;
        }
        else
        {
            for(int64_t i=0;i<PositionAfterEnd.NodeIndex;i++)
            {
                OutEndCharIndex+=Nodes[i].Length;
            }
            OutEndCharIndex+=PositionAfterEnd.CharIndex;
            OutEndCharIndex--;
        }


        CachedTextOfRange->OutStartCharIndex=OutStartCharIndex;
        CachedTextOfRange->OutEndCharIndex=OutEndCharIndex;
        CachedTextOfRange->Text.Clear();

        //append text
        if(!PositionAfterEnd.IsValid())
        {
            //reach the end of the document
            //append all the node after the start node
            int64_t Size= Nodes[PositionAtStart.NodeIndex].Length-PositionAtStart.CharIndex;
            for(int64_t i=PositionAtStart.NodeIndex+1;i<Nodes.Num();i++)
            {
                Size+=Nodes[i].Length;
            }
            CachedTextOfRange->Text.Reserve(Size);

            {
                auto& StartNode=Nodes[PositionAtStart.NodeIndex];
                auto& StartNodeBuffer = Buffers[StartNode.BufferIndex];
                int64_t StartCharIndex = StartNodeBuffer.LineStarts[StartNode.Start.LineIndex]+StartNode.Start.ColumnIndex+PositionAtStart.CharIndex;
                int64_t EndCharIndex = StartNodeBuffer.LineStarts[StartNode.End.LineIndex]+StartNode.End.ColumnIndex;
                CachedTextOfRange->Text+= StartNodeBuffer.Text.SubString(
                    StartCharIndex
                    ,EndCharIndex-StartCharIndex+1
                );
            }
            for(int64_t i=PositionAtStart.NodeIndex+1;i<Nodes.Num();i++)
            {
                auto& Node=Nodes[i];
                auto& Buffer = Buffers[Node.BufferIndex];
                CachedTextOfRange->Text+=Buffer.Text.SubString(
                    Buffer.LineStarts[Node.Start.LineIndex]+Node.Start.ColumnIndex
                    ,Node.Length
                );
            }

            return CachedTextOfRange->Text;

        }

        if(PositionAtStart.NodeIndex==PositionAfterEnd.NodeIndex)
        {
            //the start node and the end node is the same
            auto& Node=Nodes[PositionAtStart.NodeIndex];
            auto& Buffer = Buffers[Node.BufferIndex];
            CachedTextOfRange->Text+=Buffer.Text.SubString(
                Buffer.LineStarts[Node.Start.LineIndex]+Node.Start.ColumnIndex+PositionAtStart.CharIndex
                ,PositionAfterEnd.CharIndex-PositionAtStart.CharIndex+1
            );
            return CachedTextOfRange->Text;
        }

        int64_t Size= Nodes[PositionAtStart.NodeIndex].Length-PositionAtStart.CharIndex;
        for(int64_t i=PositionAtStart.NodeIndex+1;i<(PositionAfterEnd.NodeIndex-1);i++)
        {
            Size+=Nodes[i].Length;
        }
        Size+=PositionAfterEnd.CharIndex;

        CachedTextOfRange->Text.Reserve(Size);

        {
            auto& StartNode=Nodes[PositionAtStart.NodeIndex];
            auto& StartNodeBuffer = Buffers[StartNode.BufferIndex];
            int64_t StartCharIndex = StartNodeBuffer.LineStarts[StartNode.Start.LineIndex]+StartNode.Start.ColumnIndex+PositionAtStart.CharIndex;
            int64_t EndCharIndex = StartNodeBuffer.LineStarts[StartNode.End.LineIndex]+StartNode.End.ColumnIndex;
            CachedTextOfRange->Text+= StartNodeBuffer.Text.SubString(
                StartCharIndex
                ,EndCharIndex-StartCharIndex+1
            );
        }

        for(int64_t i=PositionAtStart.NodeIndex+1;i<PositionAfterEnd.NodeIndex;i++)
        {
            auto& Node=Nodes[i];
            auto& Buffer = Buffers[Node.BufferIndex];
            CachedTextOfRange->Text+=Buffer.Text.SubString(
                Buffer.LineStarts[Node.Start.LineIndex]+Node.Start.ColumnIndex
                ,Node.Length
            );
        }

        if(PositionAfterEnd.CharIndex>0)
        {
            auto& EndNode=Nodes[PositionAfterEnd.NodeIndex];
            auto& EndNodeBuffer = Buffers[EndNode.BufferIndex];
            int64_t StartCharIndex = EndNodeBuffer.LineStarts[EndNode.Start.LineIndex]+EndNode.Start.ColumnIndex;
            int64_t EndCharIndex = EndNodeBuffer.LineStarts[EndNode.End.LineIndex]+PositionAfterEnd.CharIndex-1;
            if(EndCharIndex>StartCharIndex)
            {
                CachedTextOfRange->Text+= EndNodeBuffer.Text.SubString(
                    StartCharIndex
                    ,EndCharIndex-StartCharIndex+1
                );
            }
        }

        return CachedTextOfRange->Text;
    }

    //获取指定范围的文本，不包含end,即[Start,End)
    CString GetTextIn(const SDocumentPosition & Start
                    ,const SDocumentPosition & End)
    {
        auto PositionAtStart = FindFirstNodeAtLine(Start.LineIndex);
        assert(PositionAtStart.IsValid());
        PositionAtStart.CharIndex+=Start.ColumnIndex;

        auto PositionAfterEnd = FindFirstNodeAtLine(End.LineIndex);
        assert(PositionAfterEnd.IsValid());
        PositionAfterEnd.CharIndex+=End.ColumnIndex;

        CString Text;
        int64_t Size=0;

        if(PositionAtStart.NodeIndex==PositionAfterEnd.NodeIndex)
        {
            //the start node and the end node is the same
            auto& Node=Nodes[PositionAtStart.NodeIndex];
            auto& Buffer = Buffers[Node.BufferIndex];
            Text+=Buffer.Text.SubString(
                Buffer.LineStarts[Node.Start.LineIndex]+Node.Start.ColumnIndex+PositionAtStart.CharIndex
                ,PositionAfterEnd.CharIndex-PositionAtStart.CharIndex
            );
            return Text;
        }

        Size= Nodes[PositionAtStart.NodeIndex].Length-PositionAtStart.CharIndex;
        for(int64_t i=PositionAtStart.NodeIndex+1;i<(PositionAfterEnd.NodeIndex-1);i++)
        {
            Size+=Nodes[i].Length;
        }
        Size+=PositionAfterEnd.CharIndex;

        Text.Reserve(Size);

        //拼接第一个node
        {
            auto& StartNode=Nodes[PositionAtStart.NodeIndex];
            auto& StartNodeBuffer = Buffers[StartNode.BufferIndex];
            int64_t StartCharIndex = StartNodeBuffer.LineStarts[StartNode.Start.LineIndex]+StartNode.Start.ColumnIndex+PositionAtStart.CharIndex;
            int64_t EndCharIndex = StartNodeBuffer.LineStarts[StartNode.End.LineIndex]+StartNode.End.ColumnIndex;
            Text+= StartNodeBuffer.Text.SubString(
                StartCharIndex
                ,EndCharIndex-StartCharIndex
            );
        }

        //拼接中间的node
        for(int64_t i=PositionAtStart.NodeIndex+1;i<PositionAfterEnd.NodeIndex;i++)
        {
            auto& Node=Nodes[i];
            auto& Buffer = Buffers[Node.BufferIndex];
            Text+=Buffer.Text.SubString(
                Buffer.LineStarts[Node.Start.LineIndex]+Node.Start.ColumnIndex
                ,Node.Length
            );
        }

        //拼接最后一个node
        if(PositionAfterEnd.CharIndex>0)
        {
            auto& EndNode=Nodes[PositionAfterEnd.NodeIndex];
            auto& EndNodeBuffer = Buffers[EndNode.BufferIndex];
            int64_t StartCharIndex = EndNodeBuffer.LineStarts[EndNode.Start.LineIndex]+EndNode.Start.ColumnIndex;
            int64_t EndCharIndex = EndNodeBuffer.LineStarts[EndNode.End.LineIndex]+PositionAfterEnd.CharIndex-1;
            if(EndCharIndex>StartCharIndex)
            {
                Text+= EndNodeBuffer.Text.SubString(
                    StartCharIndex
                    ,EndCharIndex-StartCharIndex
                );
            }
        }

        return Text;
    }

    //return the text, without end \n
    CString GetLineText(int64_t InLineIndex) const
    {
        int64_t LineIndex = 0;
        CString Text;
        for(auto& Node: Nodes)
        {
            int64_t DeltaLineIndex = Node.End.LineIndex-Node.Start.LineIndex;
            auto& Buffer = Buffers[Node.BufferIndex];
            int64_t EndCharIndex = Buffer.LineStarts[Node.End.LineIndex]+ Node.End.ColumnIndex;
            

            if((LineIndex+DeltaLineIndex)>=InLineIndex)
            {
                int64_t LineInBuffer=InLineIndex-LineIndex + Node.Start.LineIndex;
                int64_t StartCharIndex = Buffers[Node.BufferIndex].LineStarts[LineInBuffer];
                if(LineInBuffer==Node.Start.LineIndex)
                {
                    StartCharIndex+=Node.Start.ColumnIndex;
                }
                
                if(LineInBuffer+1>=Buffers[Node.BufferIndex].LineStarts.Num())
                {
                    int64_t EndCharIndex = Buffer.LineStarts[Node.End.LineIndex] + Node.End.ColumnIndex;
                    Text+= Buffers[Node.BufferIndex].Text.SubString(
                        StartCharIndex
                        ,EndCharIndex-StartCharIndex+1
                        );
                }
                else
                {
                    int64_t LineEndCharIndex = Buffers[Node.BufferIndex].LineStarts[LineInBuffer+1]-1;
                    int64_t NodeEndCharIndex = Buffer.LineStarts[Node.End.LineIndex] + Node.End.ColumnIndex;
                    int64_t EndCharIndex = CMathBase::Min(LineEndCharIndex,NodeEndCharIndex);
                    Text+= Buffers[Node.BufferIndex].Text.SubString(
                        StartCharIndex,
                        EndCharIndex-StartCharIndex+1);
                }

                if(Text.EndsWith(U'\n'))
                {
                    Text=Text.RemoveAt(Text.GetLength()-1);
                    return Text;
                }
            }

            LineIndex+=DeltaLineIndex;
            //if last char is '\n', the line index should add 1
            if(Buffer.Text[Buffer.LineStarts[Node.End.LineIndex]+ Node.End.ColumnIndex]==U'\n')
            {
                LineIndex++;
            } 
        
        }

        return Text;
    }

    void MoveCursorUp(
        int64_t DesiredColumnIndex
        ,SDocumentPosition & InOutCursorPosition
        )
    {
        if(InOutCursorPosition.LineIndex==0)
        {
            //the cursor is in the first line
            return;
        }

        InOutCursorPosition.LineIndex--;
        
        CString LineText=GetLineText(InOutCursorPosition.LineIndex);
        InOutCursorPosition.ColumnIndex=
            CMath::Min(DesiredColumnIndex,LineText.GetLength());

    }

    void MoveCursorDown(
        int64_t DesiredColumnIndex
        ,SDocumentPosition & InOutCursorPosition
        )
    {
        if(InOutCursorPosition.LineIndex==GetLineCount()-1)
        {
            //the cursor is in the last line
            return;
        }

        InOutCursorPosition.LineIndex++;

        CString LineText=GetLineText(InOutCursorPosition.LineIndex);
        InOutCursorPosition.ColumnIndex=
            CMath::Min(DesiredColumnIndex,LineText.GetLength());

    }

    void MoveCursorLeft(SDocumentPosition & InOutCursorPosition)
    {
        if(InOutCursorPosition.ColumnIndex==0)
        {
            if(InOutCursorPosition.LineIndex==0)
            {
                //the cursor is in the first char
                return;
            }

            InOutCursorPosition.LineIndex--;
            InOutCursorPosition.ColumnIndex=GetLineText(InOutCursorPosition.LineIndex).GetLength();
            return;
        }

        InOutCursorPosition.ColumnIndex--;
    }

    void MoveCursorRight(SDocumentPosition & InOutCursorPosition)
    {
        if(InOutCursorPosition.ColumnIndex==GetLineText(InOutCursorPosition.LineIndex).GetLength())
        {
            if(InOutCursorPosition.LineIndex==GetLineCount()-1)
            {
                //the cursor is in the last char
                return;
            }

            InOutCursorPosition.LineIndex++;
            InOutCursorPosition.ColumnIndex=0;
            return;
        }

        InOutCursorPosition.ColumnIndex++;
    }

    //is the last char of the node is the last char of the buffer
    bool IsNodeEndAlignWithBufferEnd(int64_t InNodeIndex)
    {
        auto& Node = Nodes[InNodeIndex];
        auto& Buffer = Buffers[Node.BufferIndex];
        int64_t EndCharIndex = Buffer.LineStarts[Node.End.LineIndex]+Node.End.ColumnIndex;
        return EndCharIndex==Buffer.Text.GetLength()-1;
    }

    void InsertTextAtCursor(
        const CString& InText
        ,SDocumentPosition & InOutCursorPosition
        )
    {
        if(InText.IsEmpty())
        {
            return;
        }

        auto Command= InsertTextAt(InOutCursorPosition,InText);
        AddCommand(Command);

        //更新光标位置
        {
            int64_t LastLineFeedIndex = InText.FindLast(U'\n');
            if(LastLineFeedIndex==-1)
            {
                InOutCursorPosition.ColumnIndex+=InText.GetLength();
            }
            else
            {
                int64_t LineFeedCount = CountLineFeed(InText);
                InOutCursorPosition.LineIndex+=LineFeedCount;
                InOutCursorPosition.ColumnIndex=InText.GetLength()-LastLineFeedIndex-1;
            }
        }
    }

    //delete the char before the cursor
    void BackspaceAtCursor(SDocumentPosition & InOutCursorPosition)
    {
        if(InOutCursorPosition.LineIndex==0
            &&InOutCursorPosition.ColumnIndex==0
        )
        {
            //the cursor is at the start of the document
            return;
        }

        if(Nodes.Empty())
        {
            //the document is empty
            return;
        }

        int64_t CharIndex= ToDocumentCharIndex(ToNodePosition(InOutCursorPosition));
        assert(CharIndex>0);
        auto Command =  DeleteTextAt(CharIndex-1,1);
        AddCommand(Command);

        InOutCursorPosition=ToDocumentPosition(CharIndex-1);

    }
    
    void DeleteAfterCusor(int64_t CharNumToDelete,SDocumentPosition & InOutCursorPosition)
    {
        if(CharNumToDelete<=0)
        {
            return;
        }

        int64_t CharIndex= ToDocumentCharIndex(ToNodePosition(InOutCursorPosition));
        auto Command =  DeleteTextAt(CharIndex,CharNumToDelete);
        AddCommand(Command);
    }
    
    //替换文本并更新光标位置
    void ReplaceTextAndUpdateCursor(
        const CString& OldText
        ,const CString& NewText
        ,const SDocumentPosition& OldTextStartPosition
        ,const SDocumentPosition&  OldTextEndPosition
        ,SDocumentPosition& OutCursorPosition //包含
    )
    {
        auto Command = ReplaceTextAt( 
            OldTextStartPosition
            ,OldTextEndPosition
            ,OldText
            ,NewText
        );
        AddCommand(Command);
        OutCursorPosition=ToDocumentPosition(
            ToDocumentCharIndex(OldTextStartPosition)+NewText.GetLength()
        );
    }
    
    //批量替换文本并更新光标位置
    void ReplaceTextBatchAndUpdateCursor(
        const CString& OldText
        ,const CString& NewText
        ,const TVector<SDocumentPosition>& OldTextStartPositions
        ,const TVector<SDocumentPosition>&  OldTextEndPositions
        ,SDocumentPosition& OutCursorPosition //包含
    )
    {
        auto Command = ReplaceTextBatch( 
            OldTextStartPositions
            ,OldTextEndPositions
            ,OldText
            ,NewText
        );
        AddCommand(Command);
        OutCursorPosition=ToDocumentPosition(
            ToDocumentCharIndex(OldTextStartPositions.Last())+NewText.GetLength()
        );
    }

    bool CanUndo() const 
    {
        return LastAppliedCommandIndex>=0;
    }   
    void Undo(SDocumentPosition & OutCursorPosition)
    {
        if(LastAppliedCommandIndex==-1)
        {
            return;
        }

        auto LastAppliedCommand = CommandQueue[LastAppliedCommandIndex];
        if(LastAppliedCommand->GetType()==CPieceTableCommandBase::ECommandType::Insert)
        {
            auto InsertCommand = std::static_pointer_cast<CPieceTableInsertCommand>(LastAppliedCommand);
            DeleteTextAt(InsertCommand->InsertPosition,InsertCommand->Text.GetLength());
            OutCursorPosition= ToDocumentPosition(InsertCommand->InsertPosition);

        }
        else if(LastAppliedCommand->GetType()==CPieceTableCommandBase::ECommandType::Delete)
        {
            auto DeleteCommand = std::static_pointer_cast<CPieceTableDeleteCommand>(LastAppliedCommand);
            InsertTextAt(DeleteCommand->StartCharIndex,DeleteCommand->Text);
            OutCursorPosition= ToDocumentPosition(DeleteCommand->EndCharIndex+1);
        }
        else if(LastAppliedCommand->GetType()==CPieceTableCommandBase::ECommandType::Replace)
        {
            auto ReplaceCommand = std::static_pointer_cast<CPieceTableReplaceCommand>(LastAppliedCommand);
            ReplaceTextAt(
                ReplaceCommand->StartCharIndex
                ,ReplaceCommand->StartCharIndex+ReplaceCommand->To.GetLength()-1
                ,ReplaceCommand->To
                ,ReplaceCommand->From);
            OutCursorPosition= ToDocumentPosition(ReplaceCommand->StartCharIndex+ReplaceCommand->From.GetLength());
        }
        else if (LastAppliedCommand->GetType()==CPieceTableCommandBase::ECommandType::ReplaceBatch)
        {
            auto ReplaceBatchCommand = std::static_pointer_cast<CPieceTableReplaceBatchCommand>(LastAppliedCommand);
            
            TVector<int64_t> StartCharIndices;
            TVector<int64_t> EndCharIndices;
            int64_t Diff=ReplaceBatchCommand->To.GetLength()-ReplaceBatchCommand->From.GetLength();
            for(int64_t i=0;i<ReplaceBatchCommand->StartCharIndices.Num();i++)
            {
                StartCharIndices.Add(ReplaceBatchCommand->StartCharIndices[i]+Diff*i);
                EndCharIndices.Add(ReplaceBatchCommand->EndCharIndices[i]+Diff*(i+1));
            }
            
            ReplaceTextBatch(
                StartCharIndices
                ,EndCharIndices
                ,ReplaceBatchCommand->To
                ,ReplaceBatchCommand->From);
            OutCursorPosition= ToDocumentPosition(ReplaceBatchCommand->StartCharIndices.Last()+ReplaceBatchCommand->From.GetLength());
        }

        else
        {
            //not implemented
            assert(false);
        }
        LastAppliedCommandIndex--;
    }
    bool CanRedo() const
    {
        return LastAppliedCommandIndex<(int64_t)(CommandQueue.Num()-1);
    }
    void Redo(SDocumentPosition & OutCursorPosition)
    {
        if(LastAppliedCommandIndex>=(int64_t)(CommandQueue.Num()-1))
        {
            return;
        }

        LastAppliedCommandIndex++;
        auto LastAppliedCommand = CommandQueue[LastAppliedCommandIndex];
        if(LastAppliedCommand->GetType()==CPieceTableCommandBase::ECommandType::Insert)
        {
            auto InsertCommand = std::static_pointer_cast<CPieceTableInsertCommand>(LastAppliedCommand);
            InsertTextAt(InsertCommand->InsertPosition,InsertCommand->Text);
            OutCursorPosition= ToDocumentPosition(InsertCommand->GetEndCharIndexAfterInsert());
        }
        else if(LastAppliedCommand->GetType()==CPieceTableCommandBase::ECommandType::Delete)
        {
            auto DeleteCommand = std::static_pointer_cast<CPieceTableDeleteCommand>(LastAppliedCommand);
            DeleteTextAt(DeleteCommand->StartCharIndex,DeleteCommand->Text.GetLength());
            OutCursorPosition= ToDocumentPosition(DeleteCommand->StartCharIndex);
        }
        else if(LastAppliedCommand->GetType()==CPieceTableCommandBase::ECommandType::Replace)
        {
            auto ReplaceCommand = std::static_pointer_cast<CPieceTableReplaceCommand>(LastAppliedCommand);
            ReplaceTextAt(
                ReplaceCommand->StartCharIndex
                ,ReplaceCommand->EndCharIndex
                ,ReplaceCommand->From
                ,ReplaceCommand->To);
            OutCursorPosition= ToDocumentPosition(ReplaceCommand->StartCharIndex+ReplaceCommand->To.GetLength());
        }
        else if (LastAppliedCommand->GetType()==CPieceTableCommandBase::ECommandType::ReplaceBatch)
        {
            auto ReplaceBatchCommand = std::static_pointer_cast<CPieceTableReplaceBatchCommand>(LastAppliedCommand);
            ReplaceTextBatch(
                ReplaceBatchCommand->StartCharIndices
                ,ReplaceBatchCommand->EndCharIndices
                ,ReplaceBatchCommand->From
                ,ReplaceBatchCommand->To);
            OutCursorPosition= ToDocumentPosition(ReplaceBatchCommand->EndCharIndices.Last()+ReplaceBatchCommand->To.GetLength());
        }
        else
        {
            //not implemented
            assert(false);
        }
    
    }

protected:

    bool IsNodeEndWidthLineFeed(int64_t InNodeIndex)
    {
        auto& Node = Nodes[InNodeIndex];
        auto& Buffer = Buffers[Node.BufferIndex];
        return Buffer.IsLineFeed(Node.End);
    }

    //返回buffer的索引
    int32_t AddBuffer(const CString& InText)
    {
        //检查有没有重复创建buffer
        for(int32_t BufferIndex=0;BufferIndex< Buffers.Num(); BufferIndex++)
        {
            if(Buffers[BufferIndex].Text==InText)
            {
                return BufferIndex;
            }
        }

        auto& Buffer = Buffers.Emplace();
        Buffer.Text=InText;
        Buffer.LineStarts.Add(0);
        for(int64_t i=1;i<InText.GetLength();i++)
        {
            if(InText[i-1]==U'\n')
            {
                Buffer.LineStarts.Add(i);
            }
        }

        return Buffers.Num()-1;
    }

    void UpdateNodeLength(int64_t InNodeIndex)
    {
        auto& Node = Nodes[InNodeIndex];
        auto& Buffer = Buffers[Node.BufferIndex];
        Node.Length = Buffer.LineStarts[Node.End.LineIndex]+Node.End.ColumnIndex
            -Buffer.LineStarts[Node.Start.LineIndex]-Node.Start.ColumnIndex+1;
    
        assert(Node.Length>0);
    }

    void UpdateNodeLineFeedCount(int64_t InNodeIndex)
    {
        auto& Node = Nodes[InNodeIndex];
        auto& Buffer = Buffers[Node.BufferIndex];
        Node.LineFeedCount=
            Node.End.LineIndex-Node.Start.LineIndex
            + (Buffer.IsLineFeed(Node.End)? 1:0);
    }


    int64_t CountLineFeed(const CString& InText)
    {
        int64_t LineFeedCount=0;
        for(int64_t i=0;i<InText.GetLength();i++)
        {
            if(InText[i]==U'\n')
            {
                LineFeedCount++;
            }
        }
        return LineFeedCount;
    }

    //convert DocumentPosition to NodePosition
    
    SPieceTableNodePosition ToNodePosition(const SDocumentPosition& InPosition)
    {
        int64_t LineIndex = 0;
        int64_t NodeIndex=0;
        for(;NodeIndex<Nodes.Num();NodeIndex++)
        {
            auto& Node = Nodes[NodeIndex];
            int64_t DeltaLineIndex = Node.End.LineIndex-Node.Start.LineIndex;


            if((LineIndex+DeltaLineIndex)>InPosition.LineIndex)
            {
                //the cursor is in this node
                auto& Buffer = Buffers[Node.BufferIndex];
                int64_t CursorLineIndex=InPosition.LineIndex-LineIndex+Node.Start.LineIndex;
                
                int64_t CharIndex;
                if(CursorLineIndex==Node.Start.LineIndex)
                {
                    //cursor is in the first line of this node
                    CharIndex = CMathBase::Min(
                        Buffer.LineStarts[Node.Start.LineIndex]+Node.Start.ColumnIndex+InPosition.ColumnIndex,
                        Buffer.LineStarts[CursorLineIndex+1]-1
                    )
                    -Buffer.LineStarts[Node.Start.LineIndex]-Node.Start.ColumnIndex;
                }
                else
                {
                    //cursor is in the middle of this node
                    CharIndex = CMathBase::Min(
                        Buffer.LineStarts[CursorLineIndex]+InPosition.ColumnIndex,
                        Buffer.LineStarts[CursorLineIndex+1]-1
                    )
                    -Buffer.LineStarts[Node.Start.LineIndex]-Node.Start.ColumnIndex;
                }

                assert(CharIndex>=0);
                    return SPieceTableNodePosition(
                        NodeIndex
                        ,CharIndex
                    );

            }
            else if((LineIndex+DeltaLineIndex)==InPosition.LineIndex)
            {
                //the cursor might in this node
                auto& Buffer = Buffers[Node.BufferIndex];
                int64_t LastCharIndex=Buffer.LineStarts[Node.End.LineIndex]+
                    Node.End.ColumnIndex;
                
                if(Buffer.Text[LastCharIndex]==U'\n')
                {
                    //the last char is '\n'
                    //the cursor is in last line of this node
                    int64_t CursorCharIndex=LastCharIndex; 
                    if(Node.LineFeedCount==1)
                    {
                        //the node contains only one line
                        CursorCharIndex=
                            CMathBase::Min(CursorCharIndex,
                                Buffer.LineStarts[Node.Start.LineIndex]+Node.Start.ColumnIndex+ InPosition.ColumnIndex
                            );
                    }
                    else
                    {
                        //the node contains more than one line
                        CursorCharIndex=
                            CMathBase::Min(CursorCharIndex,
                                Buffer.LineStarts[Node.End.LineIndex] + InPosition.ColumnIndex
                            );
                    }

                    int64_t CharIndex = CursorCharIndex-Buffer.LineStarts[Node.Start.LineIndex]-Node.Start.ColumnIndex;
                    assert(CharIndex>=0);
                    return SPieceTableNodePosition(
                        NodeIndex
                        ,CharIndex
                    );
                }

                int64_t CurrentColumn=0;
                if(Node.LineFeedCount==0)
                {
                    CurrentColumn=Node.Length;
                }
                else
                {
                    CurrentColumn=Node.End.ColumnIndex+1;
                }

                if(InPosition.ColumnIndex<CurrentColumn)
                {
                    int64_t CharIndex;
                    if(Node.LineFeedCount==0)
                    {
                        CharIndex = InPosition.ColumnIndex;
                    }
                    else
                    {
                        CharIndex = Buffer.LineStarts[Node.End.LineIndex]+InPosition.ColumnIndex
                            -Buffer.LineStarts[Node.Start.LineIndex]-Node.Start.ColumnIndex;
                        ;
                    }
                    
                    assert(CharIndex>=0);
                    return SPieceTableNodePosition(
                        NodeIndex
                        ,CharIndex
                    );
                }

                //the cursor is not in this node
                //search the next node
                while(InPosition.ColumnIndex>=CurrentColumn)
                {
                    NodeIndex++;
                    if(NodeIndex>=Nodes.Num())
                    {
                        return SPieceTableNodePosition(-1,0);
                    }

                    auto& Node = Nodes[NodeIndex];
                    auto& Buffer = Buffers[Node.BufferIndex];

                    if(Node.LineFeedCount==0)
                    {
                        if(CurrentColumn+Node.Length>InPosition.ColumnIndex)
                        {
                            int64_t CharIndex = InPosition.ColumnIndex-CurrentColumn;
                            assert(CharIndex>=0);
                            return SPieceTableNodePosition(
                                NodeIndex
                                ,CharIndex
                            );
                        }
                        else
                        {
                            CurrentColumn+=Node.Length;
                            continue;
                        }
                    }
                    else
                    {
                        //find first line feed
                        if(Node.End.LineIndex>Node.Start.LineIndex)
                        {
                            //the node contains more than one line
                            //so the Node.Start.LineIndex+1 is a valid line index
                            int64_t CharIndex = CMathBase::Min(
                                    Buffer.LineStarts[Node.Start.LineIndex+1]-1
                                    ,InPosition.ColumnIndex-CurrentColumn
                                );
                            assert(CharIndex>=0);
                            return SPieceTableNodePosition(
                                NodeIndex
                                ,CharIndex
                            );
                        }
                        else
                        {
                            //the node contains only one line
                            //so the end char is the line feed
                            int64_t CharIndex = CMathBase::Min(
                                    Node.End.ColumnIndex- Node.Start.ColumnIndex
                                    ,InPosition.ColumnIndex-CurrentColumn
                                );
                            assert(CharIndex>=0);

                            return SPieceTableNodePosition(
                                NodeIndex
                                ,CharIndex
                            );
                        }
                       
                    }

                }


            }

 
            LineIndex+=DeltaLineIndex;
            //check if the last char of the node is '\n'
            auto& Buffer = Buffers[Node.BufferIndex];
            int64_t LastCharIndex=Buffer.LineStarts[Node.End.LineIndex]+
                Node.End.ColumnIndex;
            if(Buffer.Text[LastCharIndex]==U'\n')
            {
                LineIndex++;
            }

            continue;
            
        }

        //at empty last line
        return SPieceTableNodePosition(-1,0);
    }

    SPieceTableNodePosition ToNodePosition(int64_t InDocumentCharIndex)
    {
        for(int64_t NodeIndex=0;NodeIndex<Nodes.Num();NodeIndex++)
        {
            auto& Node = Nodes[NodeIndex];
            if((InDocumentCharIndex+1)>Node.Length)
            {
                InDocumentCharIndex-=Node.Length;
                continue;
            }

            return SPieceTableNodePosition(NodeIndex,InDocumentCharIndex);
        }
        return SPieceTableNodePosition(-1,0);
    }

    int64_t ToDocumentCharIndex(const SPieceTableNodePosition& InNodePosition)
    {
        if(InNodePosition.NodeIndex<0)
        {
            assert(InNodePosition.CharIndex==0);
            int64_t CharIndex=0;
            for(auto& Node: Nodes)
            {
                CharIndex+=Node.Length;
            }
            return CharIndex;
        }

        int64_t CharIndex = InNodePosition.CharIndex;
        for(int64_t NodeIndex=0;NodeIndex<InNodePosition.NodeIndex;NodeIndex++)
        {
            CharIndex+=Nodes[NodeIndex].Length;
        }

        return CharIndex;
    }

    int64_t ToDocumentCharIndex(const SDocumentPosition& InDocumentPosition)
    {
        auto NodePosition = ToNodePosition(InDocumentPosition);
        return ToDocumentCharIndex(NodePosition); 
    }

    SDocumentPosition ToDocumentPosition(int64_t InDocumentCharIndex)
    {
        SDocumentPosition Position(0,0);
        for(int64_t NodeIndex=0;NodeIndex<Nodes.Num();NodeIndex++)
        {
            auto& Node = Nodes[NodeIndex];
            if((InDocumentCharIndex+1)>Node.Length)
            {
                InDocumentCharIndex-=Node.Length;
                if(Node.LineFeedCount==0)
                {
                    Position.ColumnIndex+=Node.Length;
                }
                else if(IsNodeEndWidthLineFeed(NodeIndex))
                {
                    Position.LineIndex+=Node.LineFeedCount;
                    Position.ColumnIndex=0;
                }
                else
                {
                    Position.LineIndex+=Node.LineFeedCount;
                    Position.ColumnIndex=Node.End.ColumnIndex+1;
                }
                continue;
            }

            //the char is in this node
            auto& Buffer = Buffers[Node.BufferIndex];
            auto PositionInBuffer = Buffer.GetLineAndColumnIndex(
                Buffer.LineStarts[Node.Start.LineIndex]+Node.Start.ColumnIndex+InDocumentCharIndex
            );

            int64_t DeltaLine=PositionInBuffer.LineIndex-Node.Start.LineIndex;

            if(DeltaLine>0)
            {
                Position.LineIndex+=DeltaLine;
                Position.ColumnIndex=PositionInBuffer.ColumnIndex;
            }
            else
            {
                Position.ColumnIndex+=InDocumentCharIndex;
            }
            break;

        }

        return Position;
    }

    //return the command
    std::shared_ptr<CPieceTableCommandBase> InsertTextAt(const SPieceTableNodePosition& NodePosition,const CString & InText)
    {
        CPieceTableInsertCommand* Command = new CPieceTableInsertCommand();
        Command->Text=InText;
        Command->InsertPosition=ToDocumentCharIndex(NodePosition);


        //the node that want to append at end
        //if -1, insert in a node
        int64_t AppendNodeIndex=-1;
        if(NodePosition.NodeIndex<0)
        {
            //append at document end
            if(Nodes.Empty() //the document is empty
            || Nodes.Last().BufferIndex==0 //not the original text node
            || !IsNodeEndAlignWithBufferEnd(Nodes.Num()-1)
            )
            {
                //just append a new buffer and node
                auto NewBufferIndex=AddBuffer(InText);
                auto& Buffer = Buffers[NewBufferIndex];
                auto& Node = Nodes.Emplace();
                Node.BufferIndex=NewBufferIndex;
                Node.Start.LineIndex=0;
                Node.Start.ColumnIndex=0;
                Node.End.LineIndex=Buffer.LineStarts.Num()-1;
                Node.End.ColumnIndex=Buffer.Text.GetLength()-1
                    -Buffer.LineStarts.Last();
                Node.Length=Buffer.Text.GetLength();
                UpdateNodeLineFeedCount(Nodes.Num()-1);

                PostTextChanged();
                return  std::shared_ptr<CPieceTableCommandBase>(Command);
            }
            else
            {
                AppendNodeIndex=Nodes.Num()-1;
            }

        }
        else if(NodePosition.CharIndex==0 
            && NodePosition.NodeIndex>0
            && Nodes[NodePosition.NodeIndex-1].BufferIndex>0
            && IsNodeEndAlignWithBufferEnd(NodePosition.NodeIndex-1)
            )
        {
            //try to append the text to the former node's buffer
            AppendNodeIndex=NodePosition.NodeIndex-1;
        }

        if(AppendNodeIndex>=0)
        {
            auto& Node = Nodes[AppendNodeIndex];
            auto& Buffer = Buffers[Node.BufferIndex];
            int64_t OldLength = Buffer.Text.GetLength();
            Buffer.Text+=InText;
            for(int64_t i=OldLength;i<Buffer.Text.GetLength();i++)
            {
                if(Buffer.Text[i-1]==U'\n')
                {
                    Buffer.LineStarts.Add(i);
                }
            }

            Node.End.LineIndex=Buffer.LineStarts.Num()-1;
            Node.End.ColumnIndex=Buffer.Text.GetLength()-1
                -Buffer.LineStarts.Last();
            Node.Length+=InText.GetLength();
            UpdateNodeLineFeedCount(AppendNodeIndex);

            PostTextChanged();
            return  std::shared_ptr<CPieceTableCommandBase>(Command);
        }

        if(NodePosition.CharIndex==0)
        {
            //insert at the start of the node
            //just insert a new buffer and node before the current node
            auto NewBufferIndex=AddBuffer(InText);
            auto& Buffer = Buffers[NewBufferIndex];
            Nodes.Insert(NodePosition.NodeIndex,SPieceTableNode());
            auto& Node = Nodes[NodePosition.NodeIndex];
            Node.BufferIndex=NewBufferIndex;
            Node.Start.LineIndex=0;
            Node.Start.ColumnIndex=0;
            Node.End.LineIndex=Buffer.LineStarts.Num()-1;
            Node.End.ColumnIndex=Buffer.Text.GetLength()-1
                -Buffer.LineStarts.Last();

            Node.Length=Buffer.Text.GetLength();
            UpdateNodeLineFeedCount(NodePosition.NodeIndex);

            PostTextChanged();
            return  std::shared_ptr<CPieceTableCommandBase>(Command);
        }

        //insert in the middle of the node
        //split the node into two part and insert a new buffer and node
        auto NewBufferIndex=AddBuffer(InText);

        auto& Node = Nodes[NodePosition.NodeIndex];
        auto& Buffer = Buffers[Node.BufferIndex];
        auto PositionBeforeCursor= Buffer.GetLineAndColumnIndex(
            Buffer.LineStarts[Node.Start.LineIndex]+Node.Start.ColumnIndex+NodePosition.CharIndex-1
        );

        auto PositionAfterCursor= Buffer.GetLineAndColumnIndex(
            Buffer.LineStarts[Node.Start.LineIndex]+Node.Start.ColumnIndex+NodePosition.CharIndex
        );

        SPieceTableNode FormerNode;
        FormerNode.BufferIndex=Node.BufferIndex;
        FormerNode.Start=Node.Start;
        FormerNode.End=PositionBeforeCursor;
        
        Node.Start=PositionAfterCursor;

        auto& NewBuffer=Buffers[NewBufferIndex];
        SPieceTableNode NewNode;
        NewNode.BufferIndex=NewBufferIndex;
        NewNode.Start.LineIndex=0;
        NewNode.Start.ColumnIndex=0;
        NewNode.End.LineIndex=NewBuffer.LineStarts.Num()-1;
        NewNode.End.ColumnIndex=NewBuffer.Text.GetLength()-1
            -NewBuffer.LineStarts.Last();
        NewNode.Length=NewBuffer.Text.GetLength();

        Nodes.Reserve(Nodes.Num()+2);
        Nodes.Insert(NodePosition.NodeIndex,NewNode);
        Nodes.Insert(NodePosition.NodeIndex,FormerNode);

        UpdateNodeLength(NodePosition.NodeIndex);
        //UpdateNodeLength(NodePosition.NodeIndex+1);
        UpdateNodeLength(NodePosition.NodeIndex+2);

        UpdateNodeLineFeedCount(NodePosition.NodeIndex);
        UpdateNodeLineFeedCount(NodePosition.NodeIndex+1);
        UpdateNodeLineFeedCount(NodePosition.NodeIndex+2);

        PostTextChanged();
        return  std::shared_ptr<CPieceTableCommandBase>(Command);
    }

    std::shared_ptr<CPieceTableCommandBase> InsertTextAt(const SDocumentPosition& Position,const CString & Text)
    {
        auto NodePosition = ToNodePosition(Position);
        return InsertTextAt(NodePosition,Text);
    }

    std::shared_ptr<CPieceTableCommandBase> InsertTextAt(const int64_t & Position,const CString & Text)
    {
        auto NodePosition = ToNodePosition(Position);
        return InsertTextAt(NodePosition,Text);
    }

    std::shared_ptr<CPieceTableCommandBase> DeleteTextAt(const SPieceTableNodePosition& NodePosition,int64_t CharNum)
    {
        assert(NodePosition.NodeIndex>=0&& "cant remove text from end");

        CPieceTableDeleteCommand* Command = new CPieceTableDeleteCommand();
        //Command->Text=Text;  
        Command->Text.Reserve(CharNum);
        Command->StartCharIndex=ToDocumentCharIndex(NodePosition);
        Command->EndCharIndex=Command->StartCharIndex+CharNum-1;

        int64_t LeftCharNum= CharNum;

        if(NodePosition.CharIndex+LeftCharNum< Nodes[NodePosition.NodeIndex].Length)
        {
            //all text is in this node
            if(NodePosition.CharIndex==0)
            {
                //delete from node start, just shrink the node
                auto& Node = Nodes[NodePosition.NodeIndex];
                auto& Buffer = Buffers[Node.BufferIndex];
                int64_t NewFirstCharIndex = Buffer.LineStarts[Node.Start.LineIndex]+Node.Start.ColumnIndex+LeftCharNum;
                auto OldStart=Node.Start;
                Node.Start=Buffer.GetLineAndColumnIndex(NewFirstCharIndex);
                Node.Length-=LeftCharNum;
                UpdateNodeLineFeedCount(NodePosition.NodeIndex);

                Command->Text=Buffer.Text.SubString(
                    Buffer.LineStarts[OldStart.LineIndex]+OldStart.ColumnIndex
                    ,CharNum
                );

                PostTextChanged();
                return std::shared_ptr<CPieceTableCommandBase>(Command);
            }
            else
            {
                //delete in the middle of the node
                //split the node into two part
                auto& Node = Nodes[NodePosition.NodeIndex];
                auto& Buffer = Buffers[Node.BufferIndex];

                int64_t DeletedCharStartIndex= Buffer.LineStarts[Node.Start.LineIndex]+Node.Start.ColumnIndex+NodePosition.CharIndex;
                auto PositionBeforeDelete= Buffer.GetLineAndColumnIndex(
                    DeletedCharStartIndex-1
                );

                auto PositionAfterDelete= Buffer.GetLineAndColumnIndex(
                    DeletedCharStartIndex+CharNum
                );

                SPieceTableNode NewNode;
                NewNode.BufferIndex=Node.BufferIndex;
                NewNode.Start=PositionAfterDelete;
                NewNode.End=Node.End;

                Node.End=PositionBeforeDelete;

                Nodes.Insert(NodePosition.NodeIndex+1,NewNode);

                UpdateNodeLength(NodePosition.NodeIndex);
                UpdateNodeLength(NodePosition.NodeIndex+1);

                UpdateNodeLineFeedCount(NodePosition.NodeIndex);
                UpdateNodeLineFeedCount(NodePosition.NodeIndex+1);

                Command->Text=Buffer.Text.SubString(
                    DeletedCharStartIndex, CharNum
                );

                PostTextChanged();
                return std::shared_ptr<CPieceTableCommandBase>(Command);
            }
        }
        else 
        {
            if(NodePosition.CharIndex==0)
            {
                //delete whole node
                auto& Node = Nodes[NodePosition.NodeIndex];
                Command->Text=Buffers[Node.BufferIndex].Text.SubString(
                    Buffers[Node.BufferIndex].LineStarts[Node.Start.LineIndex]+Node.Start.ColumnIndex
                    ,Node.Length
                );
                LeftCharNum-=Node.Length;
                Nodes.RemoveAt(NodePosition.NodeIndex);
            }
            else
            {
                //delete this node to node end, just shrink the node
                auto& Node = Nodes[NodePosition.NodeIndex];
                auto& Buffer = Buffers[Node.BufferIndex];
                int64_t NewLastCharIndex = Buffer.LineStarts[Node.Start.LineIndex]+Node.Start.ColumnIndex+NodePosition.CharIndex-1;
                Node.End=Buffer.GetLineAndColumnIndex(NewLastCharIndex);
                int64_t OldLength = Node.Length;
                UpdateNodeLength(NodePosition.NodeIndex);
                LeftCharNum-=OldLength-Node.Length;
                UpdateNodeLineFeedCount(NodePosition.NodeIndex);

                Command->Text=Buffer.Text.SubString(
                    Buffer.LineStarts[Node.Start.LineIndex]+Node.Start.ColumnIndex+NodePosition.CharIndex
                    ,OldLength-Node.Length
                );
            }

            if(LeftCharNum==0)
            {
                PostTextChanged();
                return std::shared_ptr<CPieceTableCommandBase>(Command);
            }
        }

        //delete the next node
        int64_t NextNodeIndex = NodePosition.NodeIndex+1;
        while(LeftCharNum>0)
        {
            if(Nodes[NextNodeIndex].Length>=LeftCharNum)
            {
                //delete the next node
                auto& NextNode=Nodes[NextNodeIndex];
                LeftCharNum-=NextNode.Length;
                Command->Text+=Buffers[NextNode.BufferIndex].Text.SubString(
                    Buffers[NextNode.BufferIndex].LineStarts[NextNode.Start.LineIndex]+NextNode.Start.ColumnIndex
                    ,NextNode.Length
                );
                Nodes.RemoveAt(NextNodeIndex);
            }
            else
            {
                break;
            }
        }

        //delete the start part of the next node
        if(LeftCharNum>0)
        {
            auto& Node = Nodes[NextNodeIndex];
            auto& Buffer = Buffers[Node.BufferIndex];
            auto OldStart=Node.Start;   
            int64_t NewFirstCharIndex = Buffer.LineStarts[Node.Start.LineIndex]+Node.Start.ColumnIndex+LeftCharNum;
            Node.Start=Buffer.GetLineAndColumnIndex(NewFirstCharIndex);
            Node.Length-=LeftCharNum;
            UpdateNodeLineFeedCount(NextNodeIndex);

            Command->Text+=Buffer.Text.SubString(
                Buffer.LineStarts[OldStart.LineIndex]+OldStart.ColumnIndex
                ,LeftCharNum
            );

        }

        PostTextChanged();
        return std::shared_ptr<CPieceTableCommandBase>(Command);

    }

    std:: shared_ptr<CPieceTableCommandBase> DeleteTextAt(int64_t DocumentCharIndex,int64_t CharNum)
    {
        auto NodePosition = ToNodePosition(DocumentCharIndex);
        return DeleteTextAt(NodePosition,CharNum);
    }

    std:: shared_ptr<CPieceTableCommandBase> ReplaceTextAt(
        const SPieceTableNodePosition& FromTextStart
        ,const SPieceTableNodePosition& FromTextEnd //包含
        ,const CString & OldText
        ,const CString & NewText)
    {
        assert(!OldText.IsEmpty() && "无法替换空文本" );
        if(NewText.IsEmpty())
        {
            return DeleteTextAt(FromTextStart,OldText.GetLength());
        }

        assert(OldText!=NewText && "无需替换");

        CPieceTableReplaceCommand* Command = new CPieceTableReplaceCommand();
        Command->From=OldText;
        Command->To=NewText;
        Command->StartCharIndex=ToDocumentCharIndex(FromTextStart);
        Command->EndCharIndex=ToDocumentCharIndex(FromTextEnd);

        auto NewBufferIndex=AddBuffer(NewText);
        auto& NewBuffer=Buffers[NewBufferIndex];
        SPieceTableNode NewTextNode;
        NewTextNode.BufferIndex=NewBufferIndex;
        NewTextNode.Start.LineIndex=0;
        NewTextNode.Start.ColumnIndex=0;
        NewTextNode.End.LineIndex=NewBuffer.LineStarts.Num()-1;
        NewTextNode.End.ColumnIndex=NewBuffer.Text.GetLength()-1
            -NewBuffer.LineStarts.Last();


        if(FromTextStart.NodeIndex==FromTextEnd.NodeIndex)
        {
            //被替换的文本在同一个节点内
            auto& Node = Nodes[FromTextStart.NodeIndex];
            auto& Buffer = Buffers[Node.BufferIndex];

            auto PositionAtEnd = Buffer.GetLineAndColumnIndex(
                Buffer.LineStarts[Node.Start.LineIndex]+Node.Start.ColumnIndex+FromTextEnd.CharIndex
            );

            if(FromTextEnd.CharIndex==Node.Length-1
                &&FromTextStart.CharIndex==0
            )
            {
                //被替换的文本是整个节点
                //直接替换节点
                Nodes[FromTextStart.NodeIndex]=NewTextNode;

                UpdateNodeLength(FromTextStart.NodeIndex);
                UpdateNodeLineFeedCount(FromTextStart.NodeIndex);
            }
            else if(FromTextEnd.CharIndex==Node.Length-1)
            {
                //被替换的文本在节点的尾部
                //仅需要将节点缩短并插入新节点
                auto PositionBeforeReplace = Buffer.GetLineAndColumnIndex(
                    Buffer.LineStarts[Node.Start.LineIndex]+Node.Start.ColumnIndex+FromTextStart.CharIndex-1
                );
                Node.End=PositionBeforeReplace;

                Nodes.Insert(FromTextStart.NodeIndex+1,NewTextNode);

                UpdateNodeLength(FromTextStart.NodeIndex);
                UpdateNodeLength(FromTextStart.NodeIndex+1);

                UpdateNodeLineFeedCount(FromTextStart.NodeIndex);
                UpdateNodeLineFeedCount(FromTextStart.NodeIndex+1);

            }
            else if(FromTextStart.CharIndex==0)
            {
                //被替换的文本在节点的头部
                //仅需要将节点缩短并插入新节点
                auto PositionAfterReplace= Buffer.GetLineAndColumnIndex(
                    Buffer.LineStarts[Node.Start.LineIndex]+Node.Start.ColumnIndex+FromTextEnd.CharIndex+1
                );
                Node.Start=PositionAfterReplace;
                Nodes.Insert(FromTextStart.NodeIndex,NewTextNode);

                UpdateNodeLength(FromTextStart.NodeIndex);
                UpdateNodeLength(FromTextStart.NodeIndex+1);

                UpdateNodeLineFeedCount(FromTextStart.NodeIndex);
                UpdateNodeLineFeedCount(FromTextStart.NodeIndex+1);
            }
            else
            {
                //被替换的文本在节点中间
                //将这个节点拆分为两个，并在其中插入一个新节点
                //拆分现有节点
                SPieceTableNode NewNode;
                NewNode.BufferIndex=Node.BufferIndex;
                auto PositionAfterReplace= Buffer.GetLineAndColumnIndex(
                    Buffer.LineStarts[Node.Start.LineIndex]+Node.Start.ColumnIndex+FromTextEnd.CharIndex+1
                );
                NewNode.Start=PositionAfterReplace;
                NewNode.End=Node.End;
                auto PositionBeforeReplace = Buffer.GetLineAndColumnIndex(
                    Buffer.LineStarts[Node.Start.LineIndex]+Node.Start.ColumnIndex+FromTextStart.CharIndex-1
                );
                Node.End=PositionBeforeReplace;
                Nodes.Insert(FromTextStart.NodeIndex+1,NewNode);

                //插入替换后的节点
                Nodes.Insert(FromTextStart.NodeIndex+1,NewTextNode);

                UpdateNodeLength(FromTextStart.NodeIndex);
                UpdateNodeLength(FromTextStart.NodeIndex+1);
                UpdateNodeLength(FromTextStart.NodeIndex+2);

                UpdateNodeLineFeedCount(FromTextStart.NodeIndex);
                UpdateNodeLineFeedCount(FromTextStart.NodeIndex+1);
                UpdateNodeLineFeedCount(FromTextStart.NodeIndex+2);
            }

        }
        else
        {
            //被替换的文本跨越多个节点
            //删除中间的节点，缩短第一个节点的尾部，缩短最后一个节点的头部
            auto& FirstNode = Nodes[FromTextStart.NodeIndex];
            auto& FirstBuffer = Buffers[FirstNode.BufferIndex];
            auto FirstPositionBeforeReplace = FirstBuffer.GetLineAndColumnIndex(
                FirstBuffer.LineStarts[FirstNode.Start.LineIndex]+FirstNode.Start.ColumnIndex+FromTextStart.CharIndex
            );

            FirstNode.End=FirstPositionBeforeReplace;

            auto& LastNode = Nodes[FromTextEnd.NodeIndex];
            auto& LastBuffer = Buffers[LastNode.BufferIndex];
            auto LastPositionAfterReplace = LastBuffer.GetLineAndColumnIndex(
                LastBuffer.LineStarts[LastNode.Start.LineIndex]+LastNode.Start.ColumnIndex+FromTextEnd.CharIndex+1
            );
            LastNode.Start=LastPositionAfterReplace;

            //删除中间的节点
            Nodes.Remove(FromTextStart.NodeIndex+1,FromTextEnd.NodeIndex-FromTextStart.NodeIndex-1);

            //插入新节点
            Nodes.Insert(FromTextStart.NodeIndex+1,NewTextNode);

            UpdateNodeLength(FromTextStart.NodeIndex);
            UpdateNodeLength(FromTextStart.NodeIndex+1);
            UpdateNodeLength(FromTextStart.NodeIndex+2);

            UpdateNodeLineFeedCount(FromTextStart.NodeIndex);
            UpdateNodeLineFeedCount(FromTextStart.NodeIndex+1);
            UpdateNodeLineFeedCount(FromTextStart.NodeIndex+2);
        }
        
        PostTextChanged();
        return std::shared_ptr<CPieceTableCommandBase>(Command);
    }

    std:: shared_ptr<CPieceTableCommandBase> ReplaceTextAt(
        const SDocumentPosition& FromTextStart
        ,const SDocumentPosition& FromTextEnd //包含
        ,const CString & FromText
        ,const CString & ToText)
    {
        return ReplaceTextAt(
            ToNodePosition(FromTextStart)
            ,ToNodePosition(FromTextEnd)
            ,FromText
            ,ToText);
    }

    std:: shared_ptr<CPieceTableCommandBase> ReplaceTextAt(
        int64_t FromStartCharIndex
        ,int64_t FromEndCharIndex //包含
        ,const CString & FromText
        ,const CString & ToText)
    {
        return ReplaceTextAt(
            ToNodePosition(FromStartCharIndex)
            ,ToNodePosition(FromEndCharIndex)
            ,FromText
            ,ToText);
    }

    std:: shared_ptr<CPieceTableCommandBase> ReplaceTextBatch(
        const TVector<SDocumentPosition> & FromTextStarts
        ,const TVector<SDocumentPosition> & FromTextEnds //包含
        ,const CString & FromText
        ,const CString & ToText)
    {
        assert(FromTextStarts.Num()==FromTextEnds.Num());
        assert(FromTextStarts.Num()>0);
        assert(FromText!=ToText);

        TVector<int64_t> FromNodeStarts;
        TVector<int64_t> FromNodeEnds;

        for(int64_t i=0;i<FromTextStarts.Num();i++)
        {
            FromNodeStarts.Add(ToDocumentCharIndex(FromTextStarts[i]));
            FromNodeEnds.Add(ToDocumentCharIndex(FromTextEnds[i]));
        }

        return ReplaceTextBatch(FromNodeStarts,FromNodeEnds,FromText,ToText);
    }

    std:: shared_ptr<CPieceTableCommandBase> ReplaceTextBatch(
        const TVector<int64_t> & FromStartCharIndexes
        ,const TVector<int64_t> & FromEndCharIndexes //包含
        ,const CString & FromText
        ,const CString & ToText)
    {
        assert(FromStartCharIndexes.Num()==FromEndCharIndexes.Num());
        assert(FromStartCharIndexes.Num()>0);
        assert(FromText!=ToText);

        CPieceTableReplaceBatchCommand* Command = new CPieceTableReplaceBatchCommand();
        Command->From=FromText;
        Command->To=ToText;
        Command->StartCharIndices=FromStartCharIndexes;
        Command->EndCharIndices=FromEndCharIndexes;

        auto NewBufferIndex=AddBuffer(ToText);
        auto& NewBuffer=Buffers[NewBufferIndex];
        SPieceTableNode NewTextNode;
        NewTextNode.BufferIndex=NewBufferIndex;
        NewTextNode.Start.LineIndex=0;
        NewTextNode.Start.ColumnIndex=0;
        NewTextNode.End.LineIndex=NewBuffer.LineStarts.Num()-1;
        NewTextNode.End.ColumnIndex=NewBuffer.Text.GetLength()-1
            -NewBuffer.LineStarts.Last();

        //从最后一个开始替换,因为替换后的节点可能会影响到后面的节点
        for(int64_t ReplaceIndex=FromStartCharIndexes.Num()-1;ReplaceIndex>=0;ReplaceIndex--)
        {
            auto& FromStart = FromStartCharIndexes[ReplaceIndex];
            auto& FromEnd = FromEndCharIndexes[ReplaceIndex];

            auto FromStartNode = ToNodePosition(FromStart);
            auto FromEndNode = ToNodePosition(FromEnd);

            if(FromStartNode.NodeIndex==FromEndNode.NodeIndex)
            {
                //被替换的文本在同一个节点内
                auto& Node = Nodes[FromStartNode.NodeIndex];
                auto& Buffer = Buffers[Node.BufferIndex];

                auto PositionAtEnd = Buffer.GetLineAndColumnIndex(
                    Buffer.LineStarts[Node.Start.LineIndex]+Node.Start.ColumnIndex+FromEndNode.CharIndex
                );

                if(FromEndNode.CharIndex==Node.Length-1
                    &&FromStartNode.CharIndex==0
                )
                {
                    //被替换的文本是整个节点
                    //直接替换节点
                    Nodes[FromStartNode.NodeIndex]=NewTextNode;

                    UpdateNodeLength(FromStartNode.NodeIndex);
                    UpdateNodeLineFeedCount(FromStartNode.NodeIndex);
                }
                else if(FromEndNode.CharIndex==Node.Length-1)
                {
                    //被替换的文本在节点的尾部
                    //仅需要将节点缩短并插入新节点
                    auto PositionBeforeReplace = Buffer.GetLineAndColumnIndex(
                        Buffer.LineStarts[Node.Start.LineIndex]+Node.Start.ColumnIndex+FromStartNode.CharIndex-1
                    );
                    Node.End=PositionBeforeReplace;

                    Nodes.Insert(FromStartNode.NodeIndex+1,NewTextNode);

                    UpdateNodeLength(FromStartNode.NodeIndex);
                    UpdateNodeLength(FromStartNode.NodeIndex+1);

                    UpdateNodeLineFeedCount(FromStartNode.NodeIndex);
                    UpdateNodeLineFeedCount(FromStartNode.NodeIndex+1);

                }
                else if(FromStartNode.CharIndex==0)
                {
                    //被替换的文本在节点的头部
                    //仅需要将节点缩短并插入新节点
                    auto PositionAfterReplace= Buffer.GetLineAndColumnIndex(
                        Buffer.LineStarts[Node.Start.LineIndex]+Node.Start.ColumnIndex+FromEndNode.CharIndex+1
                    );

                    Node.Start=PositionAfterReplace;
                    Nodes.Insert(FromStartNode.NodeIndex,NewTextNode);

                    UpdateNodeLength(FromStartNode.NodeIndex);
                    UpdateNodeLength(FromStartNode.NodeIndex+1);

                    UpdateNodeLineFeedCount(FromStartNode.NodeIndex);
                    UpdateNodeLineFeedCount(FromStartNode.NodeIndex+1);
                }
                else
                {
                    //被替换的文本在节点中间
                    //将这个节点拆分为两个，并在其中插入一个新节点
                    //拆分现有节点
                    SPieceTableNode NewNode;
                    NewNode.BufferIndex=Node.BufferIndex;
                    auto PositionAfterReplace= Buffer.GetLineAndColumnIndex(
                        Buffer.LineStarts[Node.Start.LineIndex]+Node.Start.ColumnIndex+FromEndNode.CharIndex+1
                    );

                    NewNode.Start=PositionAfterReplace;
                    NewNode.End=Node.End;
                    auto PositionBeforeReplace = Buffer.GetLineAndColumnIndex(
                        Buffer.LineStarts[Node.Start.LineIndex]+Node.Start.ColumnIndex+FromStartNode.CharIndex-1
                    );
                    Node.End=PositionBeforeReplace;
                    Nodes.Insert(FromStartNode.NodeIndex+1,NewNode);

                    //插入替换后的节点
                    Nodes.Insert(FromStartNode.NodeIndex+1,NewTextNode);

                    UpdateNodeLength(FromStartNode.NodeIndex);
                    UpdateNodeLength(FromStartNode.NodeIndex+1);
                    UpdateNodeLength(FromStartNode.NodeIndex+2);

                    UpdateNodeLineFeedCount(FromStartNode.NodeIndex);
                    UpdateNodeLineFeedCount(FromStartNode.NodeIndex+1);
                    UpdateNodeLineFeedCount(FromStartNode.NodeIndex+2);

                }
            }
            else
            {
                //被替换的文本跨越多个节点
                //删除中间的节点，缩短第一个节点的尾部，缩短最后一个节点的头部
                auto& FirstNode = Nodes[FromStartNode.NodeIndex];
                auto& FirstBuffer = Buffers[FirstNode.BufferIndex];
                auto FirstPositionBeforeReplace = FirstBuffer.GetLineAndColumnIndex(
                    FirstBuffer.LineStarts[FirstNode.Start.LineIndex]+FirstNode.Start.ColumnIndex+FromStartNode.CharIndex
                );

                FirstNode.End=FirstPositionBeforeReplace;

                auto& LastNode = Nodes[FromEndNode.NodeIndex];
                auto& LastBuffer = Buffers[LastNode.BufferIndex];
                auto LastPositionAfterReplace = LastBuffer.GetLineAndColumnIndex(
                    LastBuffer.LineStarts[LastNode.Start.LineIndex]+LastNode.Start.ColumnIndex+FromEndNode.CharIndex+1
                );
                LastNode.Start=LastPositionAfterReplace;

                //删除中间的节点
                Nodes.Remove(FromStartNode.NodeIndex+1,FromEndNode.NodeIndex-FromStartNode.NodeIndex-1);

                //插入新节点
                Nodes.Insert(FromStartNode.NodeIndex+1,NewTextNode);

                UpdateNodeLength(FromStartNode.NodeIndex);
                UpdateNodeLength(FromStartNode.NodeIndex+1);
                UpdateNodeLength(FromStartNode.NodeIndex+2);

                UpdateNodeLineFeedCount(FromStartNode.NodeIndex);
                UpdateNodeLineFeedCount(FromStartNode.NodeIndex+1);
                UpdateNodeLineFeedCount(FromStartNode.NodeIndex+2);
            }

        }

        PostTextChanged();
        return std::shared_ptr<CPieceTableCommandBase>(Command);
    }


    void AddCommand(std::shared_ptr<CPieceTableCommandBase> Command)
    {
        if(LastAppliedCommandIndex==-1)
        {
            CommandQueue.Clear(false);
        }
        else if(LastAppliedCommandIndex!= int64_t(CommandQueue.Num())-1
        )
        {
            //remove the commands after LastAppliedCommandIndex
            CommandQueue.RemoveAfter(LastAppliedCommandIndex,false);
        }

        //try to merge the command
        if(!CommandQueue.Empty() && CommandQueue.Last()->GetType()==Command->GetType())
        {
            if(Command->GetType()==CPieceTableCommandBase::ECommandType::Insert)
            {
                auto LastCommand = std::static_pointer_cast<CPieceTableInsertCommand>(CommandQueue.Last());
                auto CurrentCommand = std::static_pointer_cast<CPieceTableInsertCommand>(Command);
                if(LastCommand->InsertPosition+LastCommand->Text.GetLength()==CurrentCommand->InsertPosition)
                {
                    if(CurrentCommand->Text.StartsWith(U'\n')
                      || CurrentCommand->Text.StartsWith(U'\t')
                      || CurrentCommand->Text.StartsWith(U' ')
                    )
                    {
                        //dont merge, because the text separated
                    }
                    else
                    {
                        LastCommand->Text+=CurrentCommand->Text;
                        return;
                    }
                }
            }
            else if(Command->GetType()==CPieceTableCommandBase::ECommandType::Delete)
            {
                auto LastCommand = std::static_pointer_cast<CPieceTableDeleteCommand>(CommandQueue.Last());
                auto CurrentCommand = std::static_pointer_cast<CPieceTableDeleteCommand>(Command);
                if(CurrentCommand->EndCharIndex+1==LastCommand->StartCharIndex)
                {  
                    LastCommand->StartCharIndex=CurrentCommand->StartCharIndex;
                    LastCommand->Text=CurrentCommand->Text+LastCommand->Text;
                    return;
                }
            }
            else if(
                Command->GetType()==CPieceTableCommandBase::ECommandType::Replace
                || Command->GetType()==CPieceTableCommandBase::ECommandType::ReplaceBatch
                )
            {
                //不参与合并
            }
            else
            {
                //not implemented
                assert(false);
            }
        }

        assert(LastAppliedCommandIndex+1==int64_t(CommandQueue.Num()));
        CommandQueue.Add(std::move(Command));
        LastAppliedCommandIndex=int64_t(CommandQueue.Num())-1;
    }

  

};